package cn.rengy.web.framework.web;

import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;

import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.ModelAndView;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.rengy.lang.BusinessException;
import cn.rengy.lang.ResultEntity;
import cn.rengy.tool.web._WebUtils;
import cn.rengy.tpa.wxmp.exception.WeChatException;
import cn.rengy.web.framework.sql.DbService;
import cn.rengy.web.framework.web.vo.WeChatVO;
import io.jsonwebtoken.ExpiredJwtException;
/**
 * Controller不使用try chath处理异常，在这里统一处理
 * 可以处理Controller捕获抛出的异常，不是Controller抛出的异常不能捕获
 * 其他异常由默认实现BasicErrorController处理
 * 不能处理404错误 因为DispatcherServlet的throwExceptionIfNoHandlerFound默认为false不抛出异常
 * @author rengy
 * 参考默认实现 ResponseEntityExceptionHandler
 */
@ControllerAdvice()
public class AcmeControllerExceptionHandler  {//extends ResponseEntityExceptionHandler

	private static Logger logger = LoggerFactory.getLogger(AcmeControllerExceptionHandler.class);
	@Autowired
	private DbService dbService;
	/**
     * 应用到所有@RequestMapping注解方法，在其执行之前初始化数据绑定器
     * @param binder
     */
    @InitBinder
    public void initBinder(HttpServletRequest request,WebDataBinder binder) {
    	Object target=binder.getTarget();
    	if(target instanceof WeChatVO) {
    		String appid=request.getHeader("Appid");
    		((WeChatVO)target).setAppid(appid);
    	}
    }

    /**
     * 把值绑定到Model中，使全局@RequestMapping可以获取到该值
     * @param model
     * @throws Exception 
     */
    @ModelAttribute
    public void addAttributes(HttpServletRequest request,Model model) throws Exception {
    	//改成设置js全局变量domain
    }
    
	@ExceptionHandler({BusinessException.class})
    Object handlerBusinessException(HttpServletRequest request, BusinessException ex) {
		return handleExceptionInternal(request,ex.getErrorCode(),ex.getMessage());
    }
	
	 /**
     * 参数校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    Object handleMethodArgumentNotValidException(HttpServletRequest request,MethodArgumentNotValidException e) {
        // 按需重新封装需要返回的错误信息 解析原错误信息，封装后返回，此处返回非法的字段名称error.getField()，原始值error.getRejectedValue()，错误信息
        StringJoiner sj = new StringJoiner(";");
        e.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage()));
        return handleExceptionInternal(request,1,sj.toString());
    }
    
	@ExceptionHandler({WeChatException.class})
    Object handlerWeChatException(HttpServletRequest request, WeChatException ex) {
		logger.error("微信接口异常{}:{}",ex.getErrorCode(),ex.getMessage());
		/*int errorCode=_NumberUtils.toInt(ex.getErrorCode(),0);
		if(errorCode==40164) {
			return handleExceptionInternal(request,errorCode,ex.getMessage());
		}*/
		return handleExceptionInternal(request,ex.getErrorCode(),ex.getMessage());
    }
	
	
	@ExceptionHandler({
		UnknownAccountException.class,
		IncorrectCredentialsException.class,
		ExpiredJwtException.class,
		MaxUploadSizeExceededException.class,
		UnauthorizedException.class
	})
    Object handleRuntimeException(HttpServletRequest request, Exception ex) {
		if (ex instanceof UnknownAccountException||ex instanceof IncorrectCredentialsException) {
			return handleExceptionInternal(request,1,"用户名或密码错误");
		}else if (ex instanceof ExpiredJwtException) {
			return handleExceptionInternal(request,401,"认证已过期请重新登录");
		}else if (ex instanceof MaxUploadSizeExceededException) {
			return handleExceptionInternal(request,401,"上传文件过大");
		}else if (ex instanceof UnauthorizedException) {
			return handleExceptionInternal(request,403,"没有权限访问资源");
		}else {
	        return handleUnknowException(request,ex);
		}
        
    }
	
	/**
	 * 未知异常保存数据库
	 * @param request
	 * @param ex
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	Object handleUnknowException(HttpServletRequest request, Exception ex) {
		logger.error(ExceptionUtil.stacktraceToString(ex,20000));
		//dbService.update("insert_sys_error", ex.getClass().getName(),2,ExceptionUtil.stacktraceToString(ex,10000));
		String message="未知错误";
        return handleExceptionInternal(request,1,message);
        
    }
	
	protected Object handleExceptionInternal(HttpServletRequest request,Object errcode,String message) {
		//1.若为AJAX请求 返回JSON
        if (_WebUtils.isAjaxRequest(request)) {
        	return ResponseEntity.ok(ResultEntity.fail(errcode, message));
        }
        //2.链接请求 返回View
        Map<String, Object> model=new HashMap<String,Object>(1,1);
        model.put("message", message);
        return new ModelAndView("error",model);
	}
}
